import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
  Logger,
  Optional,
} from '@nestjs/common';
import { Observable, throwError } from 'rxjs';
import { tap, catchError } from 'rxjs/operators';
import type { FastifyRequest, FastifyReply } from 'fastify';
import { RequestContextService } from '../auth/common/services/request-context.service';
import { RequestUtil } from '../common/utils';

/**
 * 审计日志数据接口
 */
export interface AuditLogData {
  userId?: string;
  username?: string;
  module?: string;
  operationType: number;
  description?: string;
  method: string;
  path: string;
  params?: string;
  body?: string;
  statusCode?: number;
  response?: string;
  errorMsg?: string;
  ip?: string;
  userAgent?: string;
  duration?: number;
}

/**
 * 审计日志服务接口
 */
export interface IAuditLogService {
  createLog(data: AuditLogData): Promise<void>;
}

/**
 * 审计日志拦截器
 *
 * 自动记录 HTTP 请求的审计日志
 * 需要配合 AuditLog 装饰器使用
 */
@Injectable()
export class AuditLogInterceptor implements NestInterceptor {
  private readonly logger = new Logger(AuditLogInterceptor.name);

  constructor(
    private readonly auditLogService: IAuditLogService,
    @Optional() private readonly requestContext?: RequestContextService
  ) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
    const request = context.switchToHttp().getRequest<FastifyRequest>();
    const response = context.switchToHttp().getResponse<FastifyReply>();
    const handler = context.getHandler();

    // 获取审计日志元数据
    const auditMeta = Reflect.getMetadata('audit-log', handler);
    if (!auditMeta) {
      return next.handle();
    }

    const startTime = Date.now();

    // 通过 RequestContextService 获取当前用户信息
    const userId = this.requestContext?.getCurrentUserId();
    const username = this.requestContext?.getCurrentUsername();

    // 构建基础日志数据
    const logData: Partial<AuditLogData> = {
      userId,
      username,
      module: auditMeta.module,
      operationType: auditMeta.operationType,
      description: auditMeta.description,
      method: request.method,
      path: request.url,
      params: this.safeStringify(request.query),
      body: this.safeStringify(request.body),
      ip: RequestUtil.getClientIp(request),
      userAgent: RequestUtil.getUserAgent(request),
    };

    return next.handle().pipe(
      tap((data) => {
        const duration = Date.now() - startTime;
        const completeLogData: AuditLogData = {
          ...logData,
          statusCode: response.statusCode,
          response: this.safeStringify(data),
          duration,
        } as AuditLogData;

        // 异步保存日志，不阻塞业务
        this.saveLogAsync(completeLogData);
      }),
      catchError((error) => {
        const duration = Date.now() - startTime;
        const completeLogData: AuditLogData = {
          ...logData,
          statusCode: error.status || 500,
          errorMsg: error.message || '未知错误',
          duration,
        } as AuditLogData;

        // 异步保存日志，不阻塞业务
        this.saveLogAsync(completeLogData);

        return throwError(() => error);
      })
    );
  }

  /**
   * 异步保存日志
   */
  private async saveLogAsync(data: AuditLogData): Promise<void> {
    try {
      await this.auditLogService.createLog(data);
    } catch (error) {
      this.logger.error('保存审计日志失败', error);
    }
  }

  /**
   * 安全地序列化对象为 JSON 字符串
   */
  private safeStringify(obj: unknown): string | undefined {
    if (!obj) return undefined;

    try {
      const str = JSON.stringify(obj);
      // 限制长度，避免存储过大的数据
      return str.length > 10000 ? str.substring(0, 10000) + '...[truncated]' : str;
    } catch {
      return '[无法序列化]';
    }
  }
}

